package stores

import (
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
	"jvm/nzpJvm/runtime_area/heap"
)

// 从操作数栈弹出一个引用类型的数据，并且存入数组指定索引位置
// 这个指令需要三个操作数
type A_ASTORE struct{ base.NoOperandsInstruction }
type B_ASTORE struct{ base.NoOperandsInstruction }
type C_ASTORE struct{ base.NoOperandsInstruction }
type D_ASTORE struct{ base.NoOperandsInstruction }
type F_ASTORE struct{ base.NoOperandsInstruction }
type I_ASTORE struct{ base.NoOperandsInstruction }
type L_ASTORE struct{ base.NoOperandsInstruction }
type S_ASTORE struct{ base.NoOperandsInstruction }

func (self *A_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopRef()
	index := stack.PopInt()
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Refs()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	// 获取元素并且压栈
	refs[index] = value
}

func (self *B_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Bytes()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = int8(value)
}

func (self *C_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Chars()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = uint16(value)
}

func (self *D_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopDouble()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Doubles()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = value
}

func (self *F_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopFloat()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Floats()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = value
}

func (self *I_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Ints()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = value
}

func (self *L_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopLong()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Longs()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = value
}

func (self *S_ASTORE) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	// 要获取数组哪个索引位置的元素
	index := stack.PopInt()
	//数组对象
	arrayObject := stack.PopRef()
	// 数组对象不可为空
	checkNotNull(arrayObject)
	// 获取数组对象内部的元素
	refs := arrayObject.Shorts()
	// 检查是否存在索引越界
	checkIndex(len(refs), index)
	refs[index] = int16(value)
}

// 检查是否索引越界
func checkIndex(arrayLength int, index int32) {
	if index < 0 || index >= int32(arrayLength) {
		panic("java.lang.ArrayOutOfBoundsException")
	}
}

func checkNotNull(object *heap.Object) {
	if object == nil {
		panic("java.lang.NullPointerException")
	}
}
